﻿using System;
using System.Collections.Generic;
using System.Linq;

using SharedGenomics.Core;
using SharedGenomics.Annotations;

namespace SharedGenomics.Workbench.Annotations
{
    /// <summary>
    /// This class contains information for a single request for an annotation - it is 'popsicle immutable' in that it can only be modified once. Upon creation 
    /// the IAnnotation value is null and the status is Pending, once either the Finish or Fail methods have been called the object cannot be modified again and 
    /// calling either of these two methods will result in an exception
    /// </summary>
    public class AnnotationInfo
    {
        public AnnotationInfo(AnnotationDescriptor descriptor)
        {
            descriptor.ThrowIfNull("descriptor");
            this.Descriptor = descriptor;
        }

        /// <summary>
        /// Notify this AnnotationInfo that its annotation request has completed.
        /// </summary>
        /// <param name="annotation">The annotation output by the annotation request</param>
        /// <exception cref="InvalidOperationException">If this AnnotationInfo has been frozen</exception>
        /// <exception cref="ArgumentNullException">If annotation is null</exception>
        /// <exception cref="ArgumentException">If the annotation is not a valid type for the <see cref="AnnotationDescriptor"/></exception>
        public void Finish(IAnnotation annotation)
        {
            annotation.ThrowIfNull("annotation");

            Type descriptorAnnotationType = this.Descriptor.AnnotationType;
            ExceptionHelper.ThrowIf<ArgumentException>(!descriptorAnnotationType.IsAssignableFrom(annotation.GetType()), String.Format("Invalid annotation type - annotation must be assignable to {0}", descriptorAnnotationType.FullName));
            this.GuardNotFrozen();

            //NOTE: it's important that these are set in this order - this object could be read/written by multiple threads and a reader must be certain that 
            //the annotation value is set if the status is finished
            this.Value = annotation;
            this.Status = AnnotationRequestStatus.Finished;
        }

        /// <summary>
        /// Notify this AnnotationInfo that its associated annotation request has failed
        /// </summary>
        public void Fail()
        {
            this.GuardNotFrozen();
            this.Status = AnnotationRequestStatus.Failed;
        }

        private void GuardNotFrozen()
        {
            ExceptionHelper.ThrowIf<InvalidOperationException>(this.Status != AnnotationRequestStatus.Pending, "This AnnotationInfo has been frozen");
        }

        /// <summary>
        /// The annotation value corresponding to this request - this is guaranteed to be non-null only if the status is 'Finished'
        /// </summary>
        public IAnnotation Value { get; private set; }
        public AnnotationRequestStatus Status { get; private set; }
        public AnnotationDescriptor Descriptor { get; private set; }
    }
}
